#include <math.h>
#include <allegro.h>
#include <stdio.h>

#define COUNTOF(x) ( sizeof(x) / sizeof((x)[0]) )
#define PI (atan(1)*4)

int main(void);
void build_allegro(void);
void build_objects(void);
void kill_allegro(void);
void precalculate(void);
void draw_ground(void);
void main_loop(void);
void process_input(void);
void paint_ground(void);

struct vertice {
  float x, y, z;
};

struct vertice ground[401*401];
struct vertice cell[7];

BITMAP *buffer;
float rotation = PI*0.25;
float elevation = 1.1;
float zoom = 300.0;
float right_x, right_y;
float front_x, front_y;
float up_x, up_y; 

float offset_x = -20.0;
float offset_y = -20.0;

int main(void)
{
  build_allegro();
  build_objects();

  main_loop();
  
  kill_allegro();
  return 0;
}
END_OF_MAIN();

void main_loop(void)
{
   while (!key[KEY_ESC])
    {
      clear_bitmap(buffer);

      process_input();
      precalculate();

      paint_ground();

      blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
      yield_timeslice();
    }
}

void paint_ground(void)
{
  float x, y, z;
  float p_x, p_y, screen_x, screen_y, screen_x2, screen_y2;
  int i, j;

  for (i = 0; i < COUNTOF(ground); i++)
  {
    x = ground[i].x + offset_x;
    y = ground[i].y + offset_y;
    z = ground[i].z;

    if (x<-3.0 || x>3.0 || y<-3.0 || y>3.0)
      continue;

    p_x = x * right_x + y * front_x + z * up_x;
    p_y = x * right_y + y * front_y + z * up_y; 

    p_x += SCREEN_W * 0.5 + offset_x;    
    p_y += SCREEN_H * 0.5 + offset_y;

/*
    putpixel(buffer, 
            (int)floor(p_x), 
            (int)floor(p_y),
            makecol(100, 100, 100));
            */

    // cell
    for (j = 0; j < COUNTOF(cell); j++)
    {
      screen_x = p_x + cell[j].x * zoom;
      screen_y = p_y + cell[j].y * zoom;

      if (j > 0) {
        line(buffer, 
             screen_x, screen_y, 
             screen_x2, screen_y2, 
             makecol(0,255,0));
      }

      screen_x2 = screen_x;
      screen_y2 = screen_y;
    }
  }

}

void process_input(void)
{
  if (key[KEY_8_PAD]) elevation += 0.01;
  if (key[KEY_2_PAD]) elevation -= 0.01;    
  if (key[KEY_PLUS_PAD] || key[KEY_EQUALS]) zoom *= 1.1;    
  if (key[KEY_MINUS_PAD] || key[KEY_MINUS]) zoom *= 0.9;

  if (key[KEY_UP]) {
    offset_x += 0.2;
    offset_y += 0.2;
  }
  if (key[KEY_DOWN]) {
    offset_x -= 0.2;
    offset_y -= 0.2;    
  }
  if (key[KEY_LEFT]) {
    offset_x += 0.1;
    offset_y -= 0.1;
  }
  if (key[KEY_RIGHT]) {
    offset_x -= 0.1;
    offset_y += 0.1;
  }
}

void precalculate(void)
{
  /* rotation */
  right_x = cos(rotation) * 1 - sin(rotation) * 0;
  right_y = sin(rotation) * 1 + cos(rotation) * 0;
  front_x = cos(rotation) * 0 - sin(rotation) * 1;
  front_y = sin(rotation) * 0 + cos(rotation) * 1;

  /* elevation */
  right_y *= cos(elevation);
  front_y *= cos(elevation);

  /* camera
   * assuming the camera never spins around its axis we
   * can leave up.x as 0. 
   */
  up_x = 0.0;
  up_y = sin(elevation);

  /* zoom */
  right_x *= zoom;
  right_y *= zoom;
  front_x *= zoom;
  front_y *= zoom;
  up_x *= zoom;
  up_y *= zoom;
}

void build_allegro(void)
{
   if (allegro_init() != 0)
      return;
   install_timer();
   install_keyboard();

   if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0) != 0) 
    {
     if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0)
      {
       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
       allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
       return;
      }
    }
   buffer = create_bitmap(SCREEN_W, SCREEN_H);
}

void kill_allegro(void)
{
   destroy_bitmap(buffer);
}

void build_objects(void)
{
  // ground
  float x, y;
  int index = 0;
  for (x = 0.0; x <= 40.0; x+=0.1)
  {
    for (y = 0.0; y <= 40.0; y+=0.1) {
      ground[index].x = (float)x;
      ground[index].y = y;
      ground[index].z = (float) (sin(x) - cos(y)) * 0.5;
      index++;
    }
  }

  // cell
  cell[0].x = -0.050;
  cell[0].y =  0.000;

  cell[1].x = -0.016;
  cell[1].y =  0.033;

  cell[2].x =  0.016;
  cell[2].y =  0.033;

  cell[3].x =  0.050;
  cell[3].y =  0.000;

  cell[4].x =  0.016;
  cell[4].y = -0.033;

  cell[5].x = -0.016;
  cell[5].y = -0.033;

  cell[6].x = -0.050;
  cell[6].y =  0.000;
}

